package com.openApi.filters;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.openApi.utils.AesUtils;
import com.openApi.utils.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 签名安全性验证
 */
@Component
public class SafetyFilter implements GlobalFilter, Ordered {

/*    @Autowired
    private FeignDeveloperClient feignDeveloperClient;*/

    private static final String CONTENT_TYPE_JSON = "application/json";

    //排除的链接
    @Value("#{'${spring.cloud.gateway.mqUrls}'.split(',')}")
    private List<String> mqUrls;

    /**
     * 数据格式：
     * appId
     * tenantId
     * timeStamp
     * signature
     * data:消费记录,List集合
     *
     * @param exchange
     * @param chain
     * @return
     */
//    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();
        //排除特殊接口 不校验
        if (!mqUrls.contains(url)) {
            return chain.filter(exchange);
        }
        ServerRequest serverRequest = new DefaultServerRequest(exchange);

        // mediaType
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        String method = exchange.getRequest().getMethodValue();
        Result result = new Result();
        Mono<String> stringMono = serverRequest.bodyToMono(String.class);
        Mono<String> modifiedBody = stringMono
                .flatMap(
                        body -> {
                            //判断是否为POST请求
                            if (null != mediaType && HttpMethod.POST.name().equalsIgnoreCase(method) && mediaType.toString().contains(CONTENT_TYPE_JSON)) {
                                if (check(body)) {
                                    result.put("code", "2000");
                                    //顺便解密字符串包括：appId、tenantId、data
                                    String jsonData = getDecryptedData(body);
                                    return Mono.just(jsonData);
                                } else {
                                    result.put("code", "4001");
                                    result.put("message", "数据不安全");
                                }
                            } else {
                                result.put("code", "4002");
                                result.put("message", "请求错误");
                            }
                            return Mono.just(body);
                        }
                );
        ServerHttpResponse response = exchange.getResponse();
        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        headers.remove(HttpHeaders.CONTENT_LENGTH);

        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
        Mono mono = bodyInserter.insert(outputMessage, new BodyInserterContext());
        return mono.then(Mono.defer(() -> {
            if (!"2000".equals(result.get("code"))) {
                return responseError(response, result);
            }
            ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
                    exchange.getRequest()) {
                @Override
                public HttpHeaders getHeaders() {
                    long contentLength = headers.getContentLength();
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.putAll(super.getHeaders());
                    if (contentLength > 0) {
                        httpHeaders.setContentLength(contentLength);
                    } else {
                        httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                    }
                    return httpHeaders;
                }

                @Override
                public Flux<DataBuffer> getBody() {
                    return outputMessage.getBody();
                }
            };

            return chain.filter(exchange.mutate().request(decorator).build());
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 安全验证
     *
     * @param body
     * @return
     */
    public boolean check(String body) {
        Map map = JSONObject.parseObject(body, Map.class);
        //正常逻辑是调用feign 数据库查询
        /*AppDTO appDTO = feignDeveloperClient.getApp((String) map.get("appId"));
        if (appDTO == null) {
            return false;
        }
        String appSecret = appDTO.getAppSecret();*/
        String appSecret = "123";
        try {
            boolean check = AesUtils.check(map, appSecret);
            return check;
        } catch (Exception e) {
            return false;
        }

    }

    public String getDecryptedData(String body) {
        Map map = JSONObject.parseObject(body, Map.class);
        String data = (String) map.get("data");//消费记录
        String appId = (String) map.get("appId");//appId
        String tenantId = (String) map.get("tenantId");//tenantId
//        AppDTO appDTO = feignDeveloperClient.getApp(appId);
//        String appSecret = appDTO.getAppSecret()
        String appSecret = "123";
        String jsonData = null;
        try {
            jsonData = AesUtils.aesPKCS7PaddingDecrypt(data, appSecret);
        } catch (Exception e) {
            e.printStackTrace();
        }
        HashMap<String, Object> resultMap = new HashMap<>();
        if (tenantId != null) {
            resultMap.put("tenantId", tenantId);
        }
        resultMap.put("appId", appId);
        List list = JSONObject.parseObject(jsonData, List.class);
        resultMap.put("data", list);
        return JSONObject.toJSONString(resultMap);
    }


    /**
     * 不安全的返回
     *
     * @param response
     * @param error
     * @return
     */

    private Mono<Void> responseError(ServerHttpResponse response, Result error) {
        // 将信息转换为 JSON
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] data = new byte[0];
        try {
            data = objectMapper.writeValueAsBytes(error);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 输出错误信息到页面
        DataBuffer buffer = response.bufferFactory().wrap(data);
//        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

}
